| Questionnaire | yes | no | df | t | p |
|---|---|---|---|---|---|
| VVIQ | 19.083 ± 0.913 | 54.188 ± 1.095 | 148 | -21.24 | 8.65e-47 |
| OSIQ_Object | 23.924 ± 0.994 | 46.561 ± 1.192 | 148 | -13.29 | 5.07e-27 |
| OSIQ_Spatial | 41.011 ± 0.865 | 45.452 ± 1.036 | 148 | -3.40 | 8.58e-04 |
| SUIS | 17.335 ± 0.749 | 37.132 ± 0.898 | 148 | -15.51 | 7.75e-33 |
Modelling report
Are there unconscious visual images in aphantasia? Development of an implicit priming paradigm
# Packages ----------------------------------------------------------------
# using a reproducible environment
renv::restore()
# the cmdstanr package for Bayesian modelling has to be installed manually
# install.packages(
# "cmdstanr",
# repos = c('https://stan-dev.r-universe.dev', getOption("repos")))
# The cmdstan backend, if not already installed, has to be installed on your
# computer first, outside of the project:
# check_cmdstan_toolchain() # check if RTools is setup
# nb_cores <- parallel::detectCores() - 1
# install_cmdstan(cores = nb_cores)
# pacman allows to check/install/load packages with a single call
# if (!require("pacman")) install.packages("pacman") # already in renv.lock
library("pacman")
# packages to load (and install if needed) -------------------------------
pacman::p_load(
here, # easy file paths
see, # theme_modern and okabeito palette
report, # reporting various info
labelled, # labelled data
quarto,
# ---- Modelling
easystats, # modelling package framework
lme4, # mixed-effects models
car, # companion to lme4
simr, # power analysis
statmod, # power analysis
emmeans, # post-hoc tests
# ---- Bayesian modelling
brms, # Bayesian regression models
tidybayes, # tidy output for brms
bayesplot, # Bayesian visualisations
cmdstanr, # Stan interface
# ---- Visualisations
qqplotr, # QQ plots
scales, # ggplot2 scales
latex2exp, # LaTeX expressions in ggplot2
ggbeeswarm, # beeswarm plots
ggpubr, # publication-ready plots
patchwork, # combining plots
# ---- Data wrangling
readxl,
openxlsx,
tidyverse # modern R ecosystem
)
# Custom functions shared across scripts ----------------------------------
source(here("scripts/_functions.R"))
# Global cosmetic theme ---------------------------------------------------
theme_set(theme_modern(base_size = 14)) # from see in easystats
# setting my favourite palettes as ggplot2 defaults
options(
ggplot2.discrete.colour = scale_colour_okabeito,
ggplot2.discrete.fill = scale_fill_okabeito,
ggplot2.continuous.colour = scale_colour_viridis_c,
ggplot2.continuous.fill = scale_fill_viridis_c
)
# Fixing a seed for reproducibility ---------------------------------------
set.seed(14051998)
# Adding all packages' citations to a .bib --------------------------------
knitr::write_bib(c(.packages()), file = here("bibliography/packages.bib"))
1 Questionnaires analyses
1.1 Group differences
Table
Plot
Code
df_q_norm |>
# -------------------------- Long format first
pivot_longer(
cols = c(VVIQ:SUIS),
names_to = "Questionnaire",
values_to = "Score"
) |>
mutate(
Questionnaire = fct_relevel(
Questionnaire,
c("VVIQ", "SUIS", "OSIQ_Object", "OSIQ_Spatial")
)) |>
# -------------------------- Plotting
ggplot(aes(
x = Questionnaire,
y = Score,
fill = aphantasia,
color = aphantasia
)) +
# accentuation of the median line at 0.5
geom_hline(
yintercept = .5,
linetype = 1,
color = "grey30",
alpha = .2
) +
# --------------------- Scores
# violin shapes for the distributions and quantiles
geom_violin(
position = position_dodge(dw),
alpha = .1,
draw_quantiles = c(.25, .5, .75)
) +
# points for means and lines for CIs
geom_pointrange(
data = norm_means,
aes(
x = Questionnaire,
y = Mean,
ymin = CI_low,
ymax = CI_high,
color = aphantasia
),
size = .75,
linewidth = 1,
position = position_dodge(dw)
) +
# --------------------- Stars and lines
# stars for significance
annotate(
geom = "text", label = "***", color = "black",
x = 1, y = .95, size = 10
) +
# dotted line that separates the different questionnaires
geom_vline(
aes(xintercept = stage(Questionnaire, after_scale = 1.5)),
linetype = 3
) +
# stars
annotate(
geom = "text", label = "***", color = "black",
x = 2, y = .95, size = 10
) +
# dotted line
geom_vline(
aes(xintercept = stage(Questionnaire, after_scale = 2.5)),
linetype = 3
) +
# stars
annotate(
geom = "text", label = "***", color = "black",
x = 3, y = .95, size = 10
) +
# dotted line
geom_vline(
aes(xintercept = stage(Questionnaire, after_scale = 3.5)),
linetype = 3
) +
# stars
annotate(
geom = "text", label = "***", color = "black",
x = 4, y = .95, size = 10
) +
# --------------------- Aesthetics
scale_color_okabeito(name = "Group: ", labels = c(" Control ", " Aphantasia")) +
scale_fill_okabeito(name = "Group: ", labels = c(" Control ", " Aphantasia")) +
scale_x_discrete(
name = "",
labels = c("VVIQ", "SUIS", "OSIQ-Object", "OSIQ-Spatial")
) +
scale_y_continuous(
name = "Standardized score",
breaks = breaks_pretty(8)
) +
theme(
panel.grid.major.x = element_blank(),
panel.grid.major.y = element_line(),
axis.text.x = element_text(size = txt),
axis.title.y = element_text(size = txt),
legend.title = element_text(size = txt_legend),
legend.text = element_text(size = txt_legend),
legend.position = "top"
)Code
ggsave(here("figures/questionnaires.png"), dpi = 600)1.2 Congruence effects correlations
Matrices
Code
corr <-
df_questionnaires |>
select(aphantasia, "Implicit effect":"SUIS") |>
correlation(method = "spearman", p_adjust = "fdr")
corr# Correlation Matrix (spearman-method)
Parameter1 | Parameter2 | rho | 95% CI | S | p
--------------------------------------------------------------------------------
Implicit effect | Explicit effect | 0.05 | [-0.11, 0.21] | 5.44e+05 | 0.571
Implicit effect | VVIQ | 0.21 | [ 0.05, 0.37] | 4.51e+05 | 0.016*
Implicit effect | OSIQ_Object | 0.26 | [ 0.10, 0.40] | 4.27e+05 | 0.003**
Implicit effect | OSIQ_Spatial | 0.05 | [-0.11, 0.21] | 5.44e+05 | 0.571
Implicit effect | SUIS | 0.27 | [ 0.11, 0.41] | 4.21e+05 | 0.002**
Explicit effect | VVIQ | 0.08 | [-0.09, 0.24] | 5.29e+05 | 0.427
Explicit effect | OSIQ_Object | 0.12 | [-0.04, 0.28] | 5.03e+05 | 0.194
Explicit effect | OSIQ_Spatial | -0.02 | [-0.18, 0.15] | 5.84e+05 | 0.831
Explicit effect | SUIS | 0.16 | [ 0.00, 0.32] | 4.82e+05 | 0.081
VVIQ | OSIQ_Object | 0.76 | [ 0.68, 0.82] | 1.40e+05 | < .001***
VVIQ | OSIQ_Spatial | 0.28 | [ 0.13, 0.43] | 4.11e+05 | 0.001**
VVIQ | SUIS | 0.84 | [ 0.79, 0.88] | 89975.33 | < .001***
OSIQ_Object | OSIQ_Spatial | 0.09 | [-0.07, 0.25] | 5.20e+05 | 0.341
OSIQ_Object | SUIS | 0.82 | [ 0.75, 0.86] | 1.06e+05 | < .001***
OSIQ_Spatial | SUIS | 0.30 | [ 0.15, 0.45] | 3.99e+05 | < .001***
p-value adjustment method: Benjamini & Hochberg (1995)
Observations: 151
Code
corr |> summary() |> plot() +
labs(title = "Correlation matrix, whole group (Spearman FDR corrected)")Code
# ggsave(here("figures/corr-mat-whole.png"), dpi = 600)Plots
Code
layout <- "
AAAABBBB
##CCCC##
"
# ((p_corr_vviq + p_corr_osiq) / p_corr_suis) +
(p_corr_vviq + p_corr_osiq + p_corr_suis) +
plot_layout(design = layout, guides = "collect") &
theme(
legend.position = "top",
legend.text = element_text(size = 20),
legend.title = element_blank(),
axis.title = element_text(size = 16)
)Code
# ggsave(here("figures/corr-plots.png"), dpi = 600)2 Accuracy analyses
We quickly checked for accuracy differences between groups with a logistic regression model.
Code
fit_acc_im <-
glmer(
formula = correct_implicit ~ (aphantasia + congruence + color)^3 + (1 | subjectid),
data = df_i_acc,
family = binomial(link = "logit"),
control = glmerControl(optimizer = "bobyqa")
)
fit_acc_ex <-
glmer(
formula = correct_explicit ~ (aphantasia + congruence + color)^3 + (1 | subjectid),
data = df_e_acc,
family = binomial(link = "logit"),
control = glmerControl(optimizer = "bobyqa")
)| AIC | AICc | BIC | R2 (cond.) | R2 (marg.) | ICC | RMSE | Sigma | Log_loss | Score_log |
|---|---|---|---|---|---|---|---|---|---|
| 2614.99 | 2615.01 | 2679.02 | 0.17 | 9.48e-03 | 0.16 | 0.18 | 1.00 | 0.13 | -Inf |
Analysis of Deviance Table (Type II Wald chisquare tests)
Response: correct_implicit
Chisq Df Pr(>Chisq)
aphantasia 1.1704 1 0.27932
congruence 0.2858 1 0.59296
color 6.3683 1 0.01162 *
aphantasia:congruence 0.2986 1 0.58479
aphantasia:color 0.5591 1 0.45463
congruence:color 0.0163 1 0.89851
aphantasia:congruence:color 0.5143 1 0.47327
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
| color | Probability | SE | 95% CI |
|---|---|---|---|
| Uncoloured | 0.97 | 3.37e-03 | (0.96, 0.98) |
| Coloured | 0.98 | 2.70e-03 | (0.97, 0.98) |
Marginal means estimated at color
| Level1 | Level2 | Odds ratio | 95% CI | SE | df | z | p |
|---|---|---|---|---|---|---|---|
| Uncoloured | Coloured | 0.73 | (0.58, 0.92) | 0.09 | Inf | -2.63 | 0.009 |
Marginal contrasts estimated at color p-values are uncorrected.
| AIC | AICc | BIC | R2 (cond.) | R2 (marg.) | ICC | RMSE | Sigma | Log_loss | Score_log |
|---|---|---|---|---|---|---|---|---|---|
| 2020.26 | 2020.28 | 2084.03 | 0.18 | 0.03 | 0.16 | 0.15 | 1.00 | 0.11 | -Inf |
Analysis of Deviance Table (Type II Wald chisquare tests)
Response: correct_explicit
Chisq Df Pr(>Chisq)
aphantasia 1.5712 1 0.2100
congruence 0.2753 1 0.5998
color 18.9615 1 1.334e-05 ***
aphantasia:congruence 0.0186 1 0.8916
aphantasia:color 0.6236 1 0.4297
congruence:color 1.1352 1 0.2867
aphantasia:congruence:color 0.2825 1 0.5951
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
| color | Probability | SE | 95% CI |
|---|---|---|---|
| Uncoloured | 0.97 | 3.15e-03 | (0.97, 0.98) |
| Coloured | 0.99 | 1.99e-03 | (0.98, 0.99) |
Marginal means estimated at color
| Level1 | Level2 | Odds ratio | 95% CI | SE | df | z | p |
|---|---|---|---|---|---|---|---|
| Uncoloured | Coloured | 0.52 | (0.39, 0.69) | 0.08 | Inf | -4.49 | < .001 |
Marginal contrasts estimated at color p-values are uncorrected.
3 Response Times analyses
3.1 Rationale
To account for the non-normal, positively skewed distributions of the RTs, we fitted Generalized Linear Mixed Models (GLMMs) with inverse Gaussian distributions. The models were implemented in the lme4 R package and integrated in tidymodels workflows using the package multilevelmod. Models with Gamma and Gaussian distributions were also fitted and compared with the AIC and BIC to ensure that we chose the best distribution available.
The models included the Group (aphantasic, control), Congruence condition (congruent or incongruent) and Color condition (color or uncolored) along with all their two and three way interactions as fixed categorical predictors, while participants have been included as grouping factors (i.e. “random effects”). The random effect structure was chosen by fitting and comparing models with every possible combination of distribution and structure (intercept by participant, congruence or color, slope by participant on congruence and/or color) aiming for the best balance between goodness of fit and parsimony. Complex random-effects structures including various slopes on the factors failed to converge to stable and reliable estimates, hence the optimal models chosen included a single by-participant random intercept.
3.2 Model fitting
The formula of the model fitted is \(RT \sim (aphantasia + congruence + color)^3 + (1|subjectid)\).
model_formula <- rt ~ (aphantasia + congruence + color)^3 + (1|subjectid)
fit_implicit <-
glmer(
formula = model_formula,
data = df_i_rt,
family = inverse.gaussian(link = "identity"),
control = glmerControl(optimizer = "bobyqa")
)
fit_explicit <-
glmer(
formula = model_formula,
data = df_e_rt,
family = inverse.gaussian(link = "identity"),
control = glmerControl(optimizer = "bobyqa")
)3.3 Model diagnostics
The quality checks of the models are displayed in Figure S3.1 and Figure S3.2 .
Code
# characteristics to check
model_checks <- c("pp_check","homogeneity", "vif", "outliers", "qq", "reqq")
check_model(
fit_implicit,
checks = model_checks,
detrend = FALSE,
residual_type = "normal"
) |>
plot()
# ggsave(here("figures/checks-implicit.png"), dpi = 600)3.4 Model summaries
| AIC | AICc | BIC | R2 (cond.) | R2 (marg.) | ICC | RMSE | Sigma |
|---|---|---|---|---|---|---|---|
| -4619.06 | -4619.03 | -4548.26 | 0.01 | 3.47e-04 | 0.01 | 0.26 | 0.48 |
Analysis of Deviance Table (Type II Wald chisquare tests)
Response: rt
Chisq Df Pr(>Chisq)
aphantasia 0.6079 1 0.435588
congruence 8.6040 1 0.003354 **
color 0.4346 1 0.509730
aphantasia:congruence 8.5799 1 0.003399 **
aphantasia:color 0.0021 1 0.963046
congruence:color 2.1571 1 0.141914
aphantasia:congruence:color 1.4468 1 0.229035
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
| AIC | AICc | BIC | R2 (cond.) | R2 (marg.) | ICC | RMSE | Sigma |
|---|---|---|---|---|---|---|---|
| -2673.61 | -2673.58 | -2603.01 | 0.03 | 7.96e-04 | 0.03 | 0.28 | 0.41 |
Analysis of Deviance Table (Type II Wald chisquare tests)
Response: rt
Chisq Df Pr(>Chisq)
aphantasia 0.0769 1 0.7815503
congruence 14.5055 1 0.0001398 ***
color 47.6344 1 5.136e-12 ***
aphantasia:congruence 10.6801 1 0.0010830 **
aphantasia:color 0.8183 1 0.3656672
congruence:color 0.2153 1 0.6426749
aphantasia:congruence:color 0.7010 1 0.4024535
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
3.5 Estimated means and contrasts
| Group | Condition | Median (ms) | SE | df | asymp.LCL | asymp.UCL |
|---|---|---|---|---|---|---|
| Control | Incongruent | 669.71 | 19.36 | Inf | 631.77 | 707.65 |
| Aphantasia | Incongruent | 676.89 | 15.84 | Inf | 645.84 | 707.93 |
| Control | Congruent | 644.38 | 19.31 | Inf | 606.53 | 682.22 |
| Aphantasia | Congruent | 675.05 | 15.83 | Inf | 644.02 | 706.08 |
| Level1 | Level2 | Difference | 95% CI | SE | df | z | p |
|---|---|---|---|---|---|---|---|
| Aphantasia Incongruent | Aphantasia Congruent | 1.84e-03 | (-0.01, 0.01) | 5.23e-03 | Inf | 0.35 | 0.725 |
| Aphantasia Incongruent | Control Congruent | 0.03 | (-0.02, 0.08) | 0.02 | Inf | 1.30 | 0.193 |
| Control Congruent | Aphantasia Congruent | -0.03 | (-0.08, 0.02) | 0.02 | Inf | -1.23 | 0.219 |
| Control Incongruent | Aphantasia Congruent | -5.34e-03 | (-0.05, 0.04) | 0.02 | Inf | -0.21 | 0.831 |
| Control Incongruent | Aphantasia Incongruent | -7.18e-03 | (-0.06, 0.04) | 0.02 | Inf | -0.29 | 0.774 |
| Control Incongruent | Control Congruent | 0.03 | ( 0.01, 0.04) | 6.13e-03 | Inf | 4.13 | < .001 |
Marginal contrasts estimated at aphantasia, congruence p-values are uncorrected.
| Group | Condition | Median (ms) | SE | df | asymp.LCL | asymp.UCL |
|---|---|---|---|---|---|---|
| Control | Incongruent | 801.78 | 33.22 | Inf | 736.68 | 866.88 |
| Aphantasia | Incongruent | 799.21 | 27.05 | Inf | 746.18 | 852.23 |
| Control | Congruent | 770.29 | 33.17 | Inf | 705.28 | 835.30 |
| Aphantasia | Congruent | 794.57 | 27.04 | Inf | 741.56 | 847.57 |
| Level1 | Level2 | Difference | 95% CI | SE | df | z | p |
|---|---|---|---|---|---|---|---|
| Aphantasia Incongruent | Aphantasia Congruent | 4.64e-03 | (-0.01, 0.02) | 5.45e-03 | Inf | 0.85 | 0.394 |
| Aphantasia Incongruent | Control Congruent | 0.03 | (-0.05, 0.11) | 0.04 | Inf | 0.68 | 0.499 |
| Control Congruent | Aphantasia Congruent | -0.02 | (-0.11, 0.06) | 0.04 | Inf | -0.57 | 0.570 |
| Control Incongruent | Aphantasia Congruent | 7.21e-03 | (-0.08, 0.09) | 0.04 | Inf | 0.17 | 0.866 |
| Control Incongruent | Aphantasia Incongruent | 2.57e-03 | (-0.08, 0.09) | 0.04 | Inf | 0.06 | 0.952 |
| Control Incongruent | Control Congruent | 0.03 | ( 0.02, 0.04) | 6.42e-03 | Inf | 4.91 | < .001 |
Marginal contrasts estimated at aphantasia, congruence p-values are uncorrected.
3.6 Visualisations
The figures below are also displayed in the main article.
Code
subj_means_im <-
df_i_rt |>
group_by(subjectid, aphantasia, congruence) |>
reframe(rt = mean(rt)*1000)
(
plot_models_full(subj_means_im, "aphantasia", emmeans_im, y_min = 400, y_max = 1110, size = 4) +
plot_models_zoomed(subj_means_im, "aphantasia", emmeans_im, size = 4) &
theme(
legend.position = "top",
legend.title = element_blank(),
legend.text = element_text(size = 20),
legend.margin = margin(0, 150, 25, 0),
axis.title.y = element_text(size = 20),
axis.text.x = element_text(size = 18)
)
) +
plot_layout(
guides = "collect",
widths = c(1, 1.2)
)
# ggsave(here("figures/model-implicit.png"), dpi = 600)Code
subj_means_ex <-
df_e_rt |>
group_by(subjectid, aphantasia, congruence) |>
reframe(rt = mean(rt)*1000)
(
plot_models_full(subj_means_ex, "aphantasia", emmeans_ex, y_min = 350, y_max = 1450, size = 4) +
plot_models_zoomed(subj_means_ex, "aphantasia", emmeans_ex, size = 4) &
theme(
legend.position = "top",
legend.title = element_blank(),
legend.text = element_text(size = 20),
legend.margin = margin(0, 150, 25, 0),
axis.title.y = element_text(size = 20),
axis.text.x = element_text(size = 18)
)
) +
plot_layout(
guides = "collect",
widths = c(1, 1.2)
)
# ggsave(here("figures/model-explicit.png"), dpi = 600)4 Finer sub-groups
Code
df_i_acc |>
select(subjectid, aphantasia, sub_group) |>
distinct() |>
group_by(aphantasia, sub_group) |>
count() |>
arrange(sub_group) |>
rename(Aphantasia = 1, "Sub-group" = 2, N = 3) |>
display()| Aphantasia | Sub-group | N |
|---|---|---|
| Control | Hyperphantasia | 1 |
| Control | Control | 57 |
| Aphantasia | Hypophantasia | 37 |
| Aphantasia | Aphantasia | 48 |
Interestingly, we have 39 participants that did not score at floor VVIQ but between 17 and 32, a score range that Reeder & Pounder (2024) have proposed to call “hypophantasia”. However, our sample comprised only 2 hyperphantasics, i.e. participants scoring above 74 (Zeman et al., 2020). These two participants will therefore be removed from the sample before performing new analyses on the aphantasic, hypophantasic and control groups.
4.1 Questionnaires analyses
Table
| Questionnaire | Control | Hypophantasia | Aphantasia |
|---|---|---|---|
| VVIQ | 53.492 ± 1.016 | 22.923 ± 1.26 | 16.049 ± 1.117 |
| OSIQ_Object | 46.09 ± 1.192 | 25.041 ± 1.478 | 23.04 ± 1.31 |
| OSIQ_Spatial | 45.467 ± 1.045 | 42.98 ± 1.295 | 39.456 ± 1.149 |
| SUIS | 36.983 ± 0.893 | 18.895 ± 1.107 | 16.102 ± 0.981 |
| Level1 | Level2 | Difference | CI | SE | t(145) | p | Questionnaire |
|---|---|---|---|---|---|---|---|
| Control | Aphantasia | 37.44 | (34.45, 40.44) | 1.51 | 24.73 | < .001 | VVIQ |
| Control | Hypophantasia | 30.57 | (27.37, 33.76) | 1.62 | 18.91 | < .001 | VVIQ |
| Hypophantasia | Aphantasia | 6.87 | ( 3.54, 10.21) | 1.69 | 4.07 | < .001 | VVIQ |
| Control | Aphantasia | 23.05 | (19.54, 26.56) | 1.78 | 12.98 | < .001 | OSIQ_Object |
| Control | Hypophantasia | 21.05 | (17.30, 24.80) | 1.90 | 11.10 | < .001 | OSIQ_Object |
| Hypophantasia | Aphantasia | 2.00 | (-1.91, 5.91) | 1.98 | 1.01 | 0.314 | OSIQ_Object |
| Control | Aphantasia | 6.01 | ( 2.93, 9.09) | 1.56 | 3.86 | < .001 | OSIQ_Spatial |
| Control | Hypophantasia | 2.49 | (-0.80, 5.77) | 1.66 | 1.50 | 0.137 | OSIQ_Spatial |
| Hypophantasia | Aphantasia | 3.52 | ( 0.09, 6.95) | 1.74 | 2.03 | 0.044 | OSIQ_Spatial |
| Control | Aphantasia | 20.88 | (18.25, 23.51) | 1.33 | 15.70 | < .001 | SUIS |
| Control | Hypophantasia | 18.09 | (15.28, 20.89) | 1.42 | 12.74 | < .001 | SUIS |
| Hypophantasia | Aphantasia | 2.79 | (-0.14, 5.72) | 1.48 | 1.88 | 0.062 | SUIS |
Plot
# A tibble: 12 × 6
Questionnaire sub_group Mean SE CI_low CI_high
<fct> <fct> <dbl> <dbl> <dbl> <dbl>
1 VVIQ Control 5.86e- 1 0.0158 0.555 0.617
2 VVIQ Hypophantasia 1.09e- 1 0.0196 0.0698 0.147
3 VVIQ Aphantasia -4.44e-16 0.0173 -0.0342 0.0342
4 OSIQ_Object Control 5.18e- 1 0.0198 0.479 0.557
5 OSIQ_Object Hypophantasia 1.68e- 1 0.0245 0.119 0.216
6 OSIQ_Object Aphantasia 1.34e- 1 0.0217 0.0909 0.176
7 OSIQ_Spatial Control 5.09e- 1 0.0174 0.474 0.543
8 OSIQ_Spatial Hypophantasia 4.68e- 1 0.0216 0.425 0.510
9 OSIQ_Spatial Aphantasia 4.05e- 1 0.0191 0.368 0.443
10 SUIS Control 5.22e- 1 0.0186 0.485 0.558
11 SUIS Hypophantasia 1.45e- 1 0.0230 0.0992 0.190
12 SUIS Aphantasia 8.33e- 2 0.0203 0.0431 0.124
Code
df_q_norm |>
filter(sub_group != "Hyperphantasia") |>
# --------------------- Long format first
pivot_longer(
cols = c(VVIQ:SUIS),
names_to = "Questionnaire",
values_to = "Score"
) |>
mutate(
Questionnaire = fct_relevel(
Questionnaire,
c("VVIQ", "SUIS", "OSIQ_Object", "OSIQ_Spatial")
)) |>
# --------------------- Plotting
ggplot(aes(
x = Questionnaire,
y = Score,
fill = sub_group,
color = sub_group
)) +
# accentuation of the median line at 0.5
geom_hline(
yintercept = .5,
linetype = 1,
color = "grey30",
alpha = .2
) +
# ---------------- Scores
# violin shapes for the distributions and quantiles
geom_violin(
position = position_dodge(dw),
alpha = .1,
draw_quantiles = c(.25, .5, .75)
) +
# points for means and lines for CIs
geom_pointrange(
data = norm_means_finer,
aes(
x = Questionnaire,
y = Mean,
ymin = CI_low,
ymax = CI_high,
color = sub_group
),
size = .75,
linewidth = 1,
position = position_dodge(dw)
) +
# ---------------- Lines
geom_vline(
aes(xintercept = stage(Questionnaire, after_scale = 1.5)),
linetype = 3
) +
geom_vline(
aes(xintercept = stage(Questionnaire, after_scale = 2.5)),
linetype = 3
) +
# dotted line
geom_vline(
aes(xintercept = stage(Questionnaire, after_scale = 3.5)),
linetype = 3
) +
# --------------- Significance
# VVIQ control vs others
plot_significance_labels(label = "***", size = 7,
x_star = 0.83, y_star = .93,
x_line = 0.66, x_line_end = 1,
y_line = .92
) +
geom_segment(
x = 0.66, xend = 1.34,
y = .91, yend = .91, linewidth = .5, color = "black") +
# VVIQ hypo vs aph
plot_significance_labels(label = "***", size = 7,
x_star = 1.17, y_star = .39,
x_line = 1, x_line_end = 1.34,
y_line = .38
) +
# SUIS control vs others
plot_significance_labels(label = "***", size = 7,
x_star = 1.83, y_star = .93,
x_line = 1.66, x_line_end = 2,
y_line = .92
) +
geom_segment(
x = 1.66, xend = 2.34,
y = .91, yend = .91, linewidth = .5, color = "black") +
# SUIS hypo vs aph
plot_significance_labels(label = "*", size = 7,
x_star = 2.17, y_star = .39,
x_line = 2, x_line_end = 2.34,
y_line = .38
) +
# OSIQ-O control vs others
plot_significance_labels(label = "***", size = 7,
x_star = 2.83, y_star = .93,
x_line = 2.66, x_line_end = 3,
y_line = .92
) +
geom_segment(
x = 2.66, xend = 3.34,
y = .91, yend = .91, linewidth = .5, color = "black") +
# OSIQ-S control vs aph only
plot_significance_labels(label = "***", size = 7,
x_star = 4, y_star = .84,
x_line = 3.66, x_line_end = 4.34,
y_line = .83
) +
# OSIQ-S hypo vs aph
plot_significance_labels(label = "*", size = 7,
x_star = 4.17, y_star = .78,
x_line = 4, x_line_end = 4.34,
y_line = .77
) +
# ----------------------------- Aesthetics
scale_color_okabeito(name = "Group: ", labels = c(" Control ", " Hypophantasia ", " Aphantasia")) +
scale_fill_okabeito(name = "Group: ", labels = c(" Control ", " Hypophantasia ", " Aphantasia")) +
scale_x_discrete(
name = "",
labels = c("VVIQ", "SUIS", "OSIQ-Object", "OSIQ-Spatial"),
expand = expansion(mult = c(0, 0))
) +
scale_y_continuous(
name = "Standardized score",
breaks = breaks_pretty(8),
expand = expansion(mult = c(0.05, 0.1))
) +
theme(
panel.grid.major.x = element_blank(),
panel.grid.major.y = element_line(),
axis.text.x = element_text(size = txt),
axis.title.y = element_text(size = txt),
legend.title = element_text(size = txt_legend),
legend.text = element_text(size = txt_legend),
legend.position = "top"
)Code
ggsave(here("figures/questionnaires-finer.png"), dpi = 600)4.2 Response Times analysis
4.3 Model fitting
Code
model_formula_finer <- rt ~ (sub_group + congruence + color)^3 + (1|subjectid)
fit_finer_i <-
glmer(
formula = model_formula_finer,
data = df_i_finer,
family = inverse.gaussian(link = "identity"),
control = glmerControl(optimizer = "bobyqa")
)
fit_finer_e <-
glmer(
formula = model_formula_finer,
data = df_e_finer,
family = inverse.gaussian(link = "identity"),
control = glmerControl(optimizer = "bobyqa")
)4.4 Model diagnostics
Code
# characteristics to check
model_checks <- c("pp_check","homogeneity", "vif", "outliers", "qq", "reqq")
check_model(
fit_finer_i,
checks = model_checks,
detrend = FALSE,
residual_type = "normal"
) |>
plot()
# ggsave(here("figures/checks-finer-implicit.png"), dpi = 600)
check_model(
fit_finer_e,
checks = model_checks,
detrend = FALSE,
residual_type = "normal"
) |>
plot()
# ggsave(here("figures/checks-finer-explicit.png"), dpi = 600)4.5 Model summaries
Code
model_performance(fit_finer_i) |> display()| AIC | AICc | BIC | R2 (cond.) | R2 (marg.) | ICC | RMSE | Sigma |
|---|---|---|---|---|---|---|---|
| -4629.18 | -4629.13 | -4530.16 | 0.02 | 6.43e-04 | 0.01 | 0.26 | 0.48 |
Code
# model_parameters(fit_finer_i) |> display()
Anova(fit_finer_i)Analysis of Deviance Table (Type II Wald chisquare tests)
Response: rt
Chisq Df Pr(>Chisq)
sub_group 1.4143 2 0.493042
congruence 8.7113 1 0.003162 **
color 0.5361 1 0.464064
sub_group:congruence 13.2326 2 0.001338 **
sub_group:color 1.2212 2 0.543029
congruence:color 1.9224 1 0.165588
sub_group:congruence:color 1.7589 2 0.415017
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
model_performance(fit_finer_e) |> display()| AIC | AICc | BIC | R2 (cond.) | R2 (marg.) | ICC | RMSE | Sigma |
|---|---|---|---|---|---|---|---|
| -2549.41 | -2549.36 | -2450.78 | 0.03 | 6.89e-04 | 0.03 | 0.28 | 0.41 |
Code
# model_parameters(fit_finer_e) |> display()
Anova(fit_finer_e)Analysis of Deviance Table (Type II Wald chisquare tests)
Response: rt
Chisq Df Pr(>Chisq)
sub_group 0.0238 2 0.9881837
congruence 11.4280 1 0.0007234 ***
color 44.0495 1 3.202e-11 ***
sub_group:congruence 8.1752 2 0.0167794 *
sub_group:color 0.4794 2 0.7868834
congruence:color 0.2415 1 0.6230973
sub_group:congruence:color 2.2646 2 0.3222926
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
4.6 Estimated means and contrasts
| Condition | Sub-group | Median (ms) | SE | df | asymp.LCL | asymp.UCL |
|---|---|---|---|---|---|---|
| Incongruent | Control | 667.63 | 19.48 | Inf | 629.45 | 705.82 |
| Congruent | Control | 642.01 | 19.43 | Inf | 603.92 | 680.10 |
| Incongruent | Hypophantasia | 668.39 | 24.03 | Inf | 621.28 | 715.49 |
| Congruent | Hypophantasia | 654.24 | 24.00 | Inf | 607.20 | 701.28 |
| Incongruent | Aphantasia | 682.92 | 20.92 | Inf | 641.92 | 723.92 |
| Congruent | Aphantasia | 691.12 | 20.93 | Inf | 650.10 | 732.13 |
| Level1 | Level2 | Difference | 95% CI | SE | df | z | p |
|---|---|---|---|---|---|---|---|
| Congruent Control | Congruent Aphantasia | -0.05 | (-0.11, 0.01) | 0.03 | Inf | -1.72 | 0.085 |
| Congruent Control | Congruent Hypophantasia | -0.01 | (-0.07, 0.05) | 0.03 | Inf | -0.40 | 0.692 |
| Congruent Control | Incongruent Aphantasia | -0.04 | (-0.10, 0.01) | 0.03 | Inf | -1.43 | 0.152 |
| Congruent Control | Incongruent Hypophantasia | -0.03 | (-0.09, 0.03) | 0.03 | Inf | -0.85 | 0.393 |
| Congruent Hypophantasia | Congruent Aphantasia | -0.04 | (-0.10, 0.03) | 0.03 | Inf | -1.16 | 0.247 |
| Congruent Hypophantasia | Incongruent Aphantasia | -0.03 | (-0.09, 0.03) | 0.03 | Inf | -0.90 | 0.367 |
| Incongruent Aphantasia | Congruent Aphantasia | -8.19e-03 | (-0.02, 0.01) | 7.04e-03 | Inf | -1.16 | 0.244 |
| Incongruent Control | Congruent Aphantasia | -0.02 | (-0.08, 0.03) | 0.03 | Inf | -0.82 | 0.411 |
| Incongruent Control | Congruent Control | 0.03 | ( 0.01, 0.04) | 6.15e-03 | Inf | 4.17 | < .001 |
| Incongruent Control | Congruent Hypophantasia | 0.01 | (-0.05, 0.07) | 0.03 | Inf | 0.43 | 0.665 |
| Incongruent Control | Incongruent Aphantasia | -0.02 | (-0.07, 0.04) | 0.03 | Inf | -0.54 | 0.592 |
| Incongruent Control | Incongruent Hypophantasia | -7.55e-04 | (-0.06, 0.06) | 0.03 | Inf | -0.02 | 0.981 |
| Incongruent Hypophantasia | Congruent Aphantasia | -0.02 | (-0.09, 0.04) | 0.03 | Inf | -0.71 | 0.475 |
| Incongruent Hypophantasia | Congruent Hypophantasia | 0.01 | ( 0.00, 0.03) | 7.79e-03 | Inf | 1.82 | 0.069 |
| Incongruent Hypophantasia | Incongruent Aphantasia | -0.01 | (-0.08, 0.05) | 0.03 | Inf | -0.46 | 0.648 |
Marginal contrasts estimated at congruence, sub_group p-values are uncorrected.
| Condition | Sub-group | Median (ms) | SE | df | asymp.LCL | asymp.UCL |
|---|---|---|---|---|---|---|
| Incongruent | Control | 805.25 | 33.85 | Inf | 738.91 | 871.59 |
| Congruent | Control | 776.89 | 33.81 | Inf | 710.64 | 843.15 |
| Incongruent | Hypophantasia | 799.65 | 40.77 | Inf | 719.74 | 879.56 |
| Congruent | Hypophantasia | 796.06 | 40.76 | Inf | 716.17 | 875.95 |
| Incongruent | Aphantasia | 798.72 | 36.34 | Inf | 727.49 | 869.96 |
| Congruent | Aphantasia | 793.26 | 36.33 | Inf | 722.05 | 864.47 |
| Level1 | Level2 | Difference | 95% CI | SE | df | z | p |
|---|---|---|---|---|---|---|---|
| Congruent Control | Congruent Aphantasia | -0.02 | (-0.11, 0.08) | 0.05 | Inf | -0.33 | 0.742 |
| Congruent Control | Congruent Hypophantasia | -0.02 | (-0.12, 0.08) | 0.05 | Inf | -0.36 | 0.717 |
| Congruent Control | Incongruent Aphantasia | -0.02 | (-0.12, 0.08) | 0.05 | Inf | -0.44 | 0.660 |
| Congruent Control | Incongruent Hypophantasia | -0.02 | (-0.13, 0.08) | 0.05 | Inf | -0.43 | 0.667 |
| Congruent Hypophantasia | Congruent Aphantasia | 2.81e-03 | (-0.10, 0.11) | 0.05 | Inf | 0.05 | 0.959 |
| Congruent Hypophantasia | Incongruent Aphantasia | -2.66e-03 | (-0.11, 0.10) | 0.05 | Inf | -0.05 | 0.961 |
| Incongruent Aphantasia | Congruent Aphantasia | 5.46e-03 | (-0.01, 0.02) | 7.44e-03 | Inf | 0.73 | 0.463 |
| Incongruent Control | Congruent Aphantasia | 0.01 | (-0.09, 0.11) | 0.05 | Inf | 0.24 | 0.809 |
| Incongruent Control | Congruent Control | 0.03 | ( 0.02, 0.04) | 6.58e-03 | Inf | 4.31 | < .001 |
| Incongruent Control | Congruent Hypophantasia | 9.19e-03 | (-0.09, 0.11) | 0.05 | Inf | 0.17 | 0.862 |
| Incongruent Control | Incongruent Aphantasia | 6.53e-03 | (-0.09, 0.10) | 0.05 | Inf | 0.13 | 0.895 |
| Incongruent Control | Incongruent Hypophantasia | 5.60e-03 | (-0.10, 0.11) | 0.05 | Inf | 0.11 | 0.916 |
| Incongruent Hypophantasia | Congruent Aphantasia | 6.39e-03 | (-0.10, 0.11) | 0.05 | Inf | 0.12 | 0.907 |
| Incongruent Hypophantasia | Congruent Hypophantasia | 3.59e-03 | (-0.01, 0.02) | 8.01e-03 | Inf | 0.45 | 0.654 |
| Incongruent Hypophantasia | Incongruent Aphantasia | 9.30e-04 | (-0.11, 0.11) | 0.05 | Inf | 0.02 | 0.986 |
Marginal contrasts estimated at congruence, sub_group p-values are uncorrected.
Code
subj_means_finer_im <-
df_i_finer |>
group_by(subjectid, sub_group, congruence) |>
reframe(rt = mean(rt)*1000)
(
plot_models_full(subj_means_finer_im, "sub_group", emmeans_finer_im, size = 4, dw = .6) +
plot_models_zoomed(subj_means_finer_im, "sub_group", emmeans_finer_im, size = 4) &
theme(
legend.position = "top",
legend.title = element_blank(),
legend.text = element_text(size = 20),
legend.margin = margin(0, 150, 25, 0),
axis.title.y = element_text(size = 20),
axis.text.x = element_text(size = 18)
)
) +
plot_layout(
guides = "collect",
widths = c(1, 1.2)
)
# ggsave(here("figures/model-implicit-finer.png"), dpi = 600)Code
subj_means_finer_ex <-
df_e_finer |>
group_by(subjectid, sub_group, congruence) |>
reframe(rt = mean(rt)*1000)
(
plot_models_full(subj_means_finer_ex, "sub_group", emmeans_finer_ex, y_min = 350, y_max = 1450, size = 4, dw = .6) +
plot_models_zoomed(subj_means_finer_ex, "sub_group", emmeans_finer_ex, size = 4) &
theme(
legend.position = "top",
legend.title = element_blank(),
legend.text = element_text(size = 20),
legend.margin = margin(0, 150, 25, 0),
axis.title.y = element_text(size = 20),
axis.text.x = element_text(size = 18)
)
) +
plot_layout(
guides = "collect",
widths = c(1, 1.2)
)
# ggsave(here("figures/model-explicit-finer.png"), dpi = 600)5 Bayesian modelling
We used Bayesian hypothesis testing to assess the congruence effect separately for each group. Thus, for each subset of the data (control, aphantasia, hypophantasia, aphantasia without hypo., control without hyperphantasia), we fitted a model with the formula \(RT \sim (congruence + color)^3 + (1|subjectid)\) and compared the models with and without the congruence effect using Bayes Factors.
formula <- bf(rt ~ congruence * color + (1 | subjectid))We used more sensible priors than the default ones as regularizing priors to avoid overfitting.
# to see the default priors:
# get_prior(formula, data = df_i_rt, family = shifted_lognormal())
# need to define better priors for the intercept, sigma, b, and sd
prior <- c(
prior(normal(-1, 0.5), class = 'Intercept'), # around exp(-1) = 0.36 secs
prior(normal(0.4, 0.3), class = 'sigma'), # SD of individual rts in log-units
prior(normal(0, 0.3), class = 'b'), # around exp(-1) - exp(-1 + 0.3) = 150 ms
prior(normal(0.3, 0.1), class = 'sd') # some variability between participants
)Bayesian models were fitted using the brms package with the cmdstanr backend. We used parallel processing to fit the models. We aimed for 40000 post-warmup iterations to have sufficient draws to compute reliable Bayes Factors. Using a computer with 20 cores available for parallel processing, we could fit 20 chains of 3000 iterations with 1000 warmup iterations. Each model fitted in approximately 1 minute. Model fits are saved as .rds files for reproducibility and can be found on OSF in the analysis project, under the data/r-data-structures folder.
Code
# detecting the number of cores to use
n_cores <- parallel::detectCores() - 4
# defining the number of iterations per chain (+ 1000 warm-up)
n_iter <- ceiling(40000 / n_cores) + 1000
# cmdstanr options
options(cmdstanr_warn_inits = FALSE)Fitting the first model
# Implicit task, aphantasia group ---------------------------------------------
fit_implicit_aph <-
brm(
formula = formula,
data = df_i_rt |> filter(aphantasia == "Aphantasia"),
family = shifted_lognormal(),
prior = prior,
sample_prior = TRUE,
chains = n_cores,
cores = n_cores,
iter = n_iter,
warmup = 1000,
refresh = 5,
backend = "cmdstanr",
stan_model_args = list(stanc_options = list("O1")),
save_pars = save_pars(all = TRUE),
file = here("data/r-data-structures/brms-model-implicit-aph.rds"),
file_compress = "xz"
)To save compilation time, we used the brms function update to use the first compiled model as a template for all the others.
Updating the first model for the other groups
# Implicit task models ----------------------------------------------------
# Control group ---------------------------------------------
fit_implicit_con <-
# the `custom_update` function can be found in the `scripts/_functions.R` file
custom_update(fit_implicit_aph,
data = df_i_rt |> filter(aphantasia == "Control"),
file = "data/r-data-structures/brms-model-implicit-con.rds"
)
# Finer aphantasia group -------------------------------------
fit_implicit_finer_aph <-
custom_update(fit_implicit_aph,
data = df_i_finer |> filter(sub_group == "Aphantasia"),
file = "data/r-data-structures/brms-model-implicit-finer-aph.rds"
)
# Hypophantasia group -------------------------------------
fit_implicit_hypo <-
custom_update(fit_implicit_aph,
data = df_i_finer |> filter(sub_group == "Hypophantasia"),
file = "data/r-data-structures/brms-model-implicit-hypo.rds"
)
# Finer control group -------------------------------------
fit_implicit_finer_con <-
custom_update(fit_implicit_aph,
data = df_i_finer |> filter(sub_group == "Control"),
file = "data/r-data-structures/brms-model-implicit-finer-con.rds"
)
# Explicit task models -----------------------------------------------------
# Aphantasia group ---------------------------------------------
fit_explicit_aph <-
custom_update(fit_implicit_aph,
data = df_e_rt |> filter(aphantasia == "Aphantasia"),
file = "data/r-data-structures/brms-model-explicit-aph.rds"
)
# Control group ---------------------------------------------
fit_explicit_con <-
custom_update(fit_implicit_aph,
data = df_e_rt |> filter(aphantasia == "Control"),
file = "data/r-data-structures/brms-model-explicit-con.rds"
)
# Finer aphantasia group -------------------------------------
fit_explicit_finer_aph <-
custom_update(fit_implicit_aph,
data = df_e_finer |> filter(sub_group == "Aphantasia"),
file = "data/r-data-structures/brms-model-explicit-finer-aph.rds"
)
# Hypophantasia group -------------------------------------
fit_explicit_hypo <-
custom_update(fit_implicit_aph,
data = df_e_finer |> filter(sub_group == "Hypophantasia"),
file = "data/r-data-structures/brms-model-explicit-hypo.rds"
)
# Finer control group -------------------------------------
fit_explicit_finer_con <-
custom_update(fit_implicit_aph,
data = df_e_finer |> filter(sub_group == "Control"),
file = "data/r-data-structures/brms-model-explicit-finer-con.rds"
)We finally compute Bayes Factor in favour of the null model of an absence of congruence effect for each group.
Implicit task models ------------------------------------------------
Aphantasia group, evidence against an implicit congruence effect:
BF_01 = 14.12439
Control group, evidence against an implicit congruence effect:
BF_01 = 2.014823e-06
Finer aphantasia group, evidence against an implicit congruence effect:
BF_01 = 12.59357
Hypophantasia group, evidence against an implicit congruence effect:
BF_01 = 2.413134
Finer control group, evidence against an implicit congruence effect:
BF_01 = 0.004105608
Explicit task models ------------------------------------------------
Aphantasia group, evidence against an explicit congruence effect:
BF_01 = 12.08795
Control group, evidence against an explicit congruence effect:
BF_01 = 0.002964782
Finer aphantasia group, evidence against an explicit congruence effect:
BF_01 = 11.78268
Hypophantasia group, evidence against an explicit congruence effect:
BF_01 = 9.81433
Finer control group, evidence against an explicit congruence effect:
BF_01 = 2.179724e-06
═════════════════════════════════════════════════════════════════════════
Analyses were conducted using the R Statistical language (version 4.4.1; R Core
Team, 2024) on Windows 11 x64 (build 22631)
Packages used:
- quarto (version 1.4.4; Allaire J, Dervieux C, 2024)
- qqplotr (version 0.0.6; Almeida A et al., 2018)
- lme4 (version 1.1.35.5; Bates D et al., 2015)
- Matrix (version 1.7.0; Bates D et al., 2024)
- effectsize (version 0.8.9; Ben-Shachar MS et al., 2020)
- brms (version 2.21.0; Bürkner P, 2017)
- ggbeeswarm (version 0.7.2; Clarke E et al., 2023)
- Rcpp (version 1.0.12; Eddelbuettel D et al., 2024)
- car (version 3.1.2; Fox J, Weisberg S, 2019)
- carData (version 3.0.5; Fox J et al., 2022)
- cmdstanr (version 0.8.1; Gabry J et al., 2024)
- bayesplot (version 1.11.1; Gabry J, Mahr T, 2024)
- statmod (version 1.5.0; Giner G, Smyth GK, 2016)
- simr (version 1.0.7; Green P, MacLeod CJ, 2016)
- lubridate (version 1.9.3; Grolemund G, Wickham H, 2011)
- ggpubr (version 0.6.0; Kassambara A, 2023)
- tidybayes (version 3.0.6; Kay M, 2023)
- labelled (version 2.13.0; Larmarange J, 2024)
- emmeans (version 1.10.3; Lenth R, 2024)
- parameters (version 0.22.0; Lüdecke D et al., 2020)
- performance (version 0.12.0; Lüdecke D et al., 2021)
- easystats (version 0.7.2; Lüdecke D et al., 2022)
- see (version 0.8.4; Lüdecke D et al., 2021)
- insight (version 0.20.1; Lüdecke D et al., 2019)
- bayestestR (version 0.13.2; Makowski D et al., 2019)
- modelbased (version 0.8.8; Makowski D et al., 2020)
- report (version 0.5.8; Makowski D et al., 2023)
- correlation (version 0.8.5; Makowski D et al., 2022)
- latex2exp (version 0.9.6; Meschiari S, 2022)
- here (version 1.0.1; Müller K, 2020)
- tibble (version 3.2.1; Müller K, Wickham H, 2023)
- datawizard (version 0.11.0; Patil I et al., 2022)
- patchwork (version 1.2.0; Pedersen T, 2024)
- R (version 4.4.1; R Core Team, 2024)
- pacman (version 0.5.1; Rinker TW, Kurkiewicz D, 2018)
- openxlsx (version 4.2.5.2; Schauberger P, Walker A, 2023)
- ggplot2 (version 3.5.1; Wickham H, 2016)
- forcats (version 1.0.0; Wickham H, 2023)
- stringr (version 1.5.1; Wickham H, 2023)
- tidyverse (version 2.0.0; Wickham H et al., 2019)
- readxl (version 1.4.3; Wickham H, Bryan J, 2023)
- dplyr (version 1.1.4; Wickham H et al., 2023)
- purrr (version 1.0.2; Wickham H, Henry L, 2023)
- readr (version 2.1.5; Wickham H et al., 2024)
- scales (version 1.3.0; Wickham H et al., 2023)
- tidyr (version 1.3.1; Wickham H et al., 2024)
═════════════════════════════════════════════════════════════════════════